Skip to main content

Mouse and keyboard

In this section, we discuss how a user can interact with dynamic elements, providing different types of input.

UI Elements

A core package, wljs-inputs, provides a set of basic UI elements used for creating buttons, sliders, text fields, and more.

info

If you need a custom element, you can create it directly in the notebook using WLX or JavaScript cell types. Please see the guide: Emitting events.

The following elements are available out of the box:

And for grouping elements:

Examples

Each standard input element is an EventObject, to which you can assign any handler function. You don't necessarily need to assign it to a variable. For example (see InputButton):

EventHandler[InputButton["Click"], Beep]

is equally valid as:

btn = InputButton["Click"];
EventHandler[btn, Beep];

btn

Here are some other examples:

EventHandler[InputRange[0, 1, 0.1], Function[value, radius = value]]
% // EventFire; (* just to initialize `radius` *)

Graphics[{LightBlue, Disk[], Pink, Disk[{0,0}, radius // Offload]}]
tip

Apply EventFire to any EventObject to manually fire an event with a default value to initialize your variables, if needed.

You can also add a label to an InputRange:

InputRange[0, 1, 0.1, "Label" -> "Radius"]

And an initial value as the fourth argument:

InputRange[0, 1, 0.1, 0.7, "Label" -> "Radius"]

Here is an example using InputSelect:

angle = 45 Degree;
EventHandler[InputSelect[{Pi/2 -> "90", Pi/4 -> "45", 0 -> "0"}, Pi/4], Function[value, angle = value]]

Graphics[{Rotate[Rectangle[{0,0}, {1,1}], angle // Offload]}]

Here is a simple text input:

text = "Example";
EventHandler[InputText[], Function[value, text = value]]

Graphics[Table[{
Hue[i/10., 1., 1.], Rotate[Text[Style[text // Offload, FontSize -> RandomInteger[{12, 24}]], RandomReal[{-1,1}, 2]], RandomChoice[{Pi, Pi/4, Pi/2, 0}]]
}, {i, 10}]]

Grouping Input Elements

If you only need visual grouping, consider using Grid, Row, or Column, e.g.:

slider = InputRange[0, 1, 0.1]; 
button = InputButton[];

{slider, button} // Column

Another way to group at the event level is using InputGroup:

slider = InputRange[0, 1, 0.1]; 
button = InputButton[];

joined = InputGroup[<|"Button" -> button, "Slider" -> slider|>, "Label" -> "Group"];
EventHandler[joined, Print]

It merges an association (as above) or a list of EventObjects into a new one. You do not need to assign separate EventHandlers—just one joined handler is enough. It fires an event preserving the original structure of the association or list:

Payload
<|"Slider" -> 0.5, "Button" -> True|>

Joining Different Events

You can also merge event objects underlying UI elements using Join. Here’s a simple example:

button = InputButton[]
slider = InputRange[0, 1, 0.1]

EventHandler[Join[button, slider], Function[data,
Print[data]
]];

You’ll get either:

True

Or:

0.5

Depending on which element triggered the event. To distinguish them, use topics (see EventObject):

button = InputButton["Topic" -> "Button"]
slider = InputRange[0, 1, 0.1, "Topic" -> "Slider"]

EventHandler[Join[button, slider], {type_ :> Function[data,
Print[type <> ":" <> ToString[data]]
]}];

Or capture them individually:

button = InputButton["Topic" -> "Button"]
slider = InputRange[0, 1, 0.1, "Topic" -> "Slider"]

EventHandler[Join[button, slider], {
"Button" -> Beep,
"Slider" -> Print
}];

The slider prints a message; the button triggers a beep.

Chaining Events

Most GUI elements support chaining, where each reuses the same EventObject. It’s passed as the first argument:

ev = EventObject[];

InputButton[ev, "Topic" -> "Button"]
InputRange[ev, 0, 1, 0.1, "Topic" -> "Slider"]

EventHandler[ev, {
"Button" -> Beep,
"Slider" -> Print
}];

With this approach, there's no need to create and join new events. This leaves a smaller footprint and reduces overhead.

2D Graphics

Some primitives, as well as the entire canvas, support the EventHandler method. Let's start with Graphics itself.

Graphics as an Event Generator

You can attach event handlers to a Graphics expression, which represents the SVG container of your 2D graph.

This approach offers some benefits over using Primitives. For instance, events like "mousemove" or "click" are captured even if other objects are layered on top. The following event patterns (or topics for EventHandler) are supported:

  • "keydown": Captures keyboard input after the canvas is clicked
  • "capturekeydown": Same as above but also prevents page scrolling
  • "mousemove": Captures mouse movement
  • "click": Captures clicks (without the Alt key)
  • "altclick": Captures clicks with the Alt key held

Example:

pt = {};
EventHandler[
Graphics[{
PointSize[0.05], Blue, Opacity[0.5],
Point[pt // Offload]
}, PlotRange -> {{-1, 1}, {-1, 1}}],
{"mousemove" -> Function[xy, pt = Append[pt, xy]]}
]

One can do the same with Plot, since it produces Graphics symbol.

Alternative Way

An alternative approach to assigning handlers:

pt = {};
Graphics[{
PointSize[0.05], Blue, Opacity[0.5],
Point[pt // Offload],
EventHandler[Null, {"mousemove" -> Function[xy, pt = Append[pt, xy]]}]
}, PlotRange -> {{-1, 1}, {-1, 1}}]

When EventHandler has Null as its argument, it attaches to the nearest parent. Similarly, you can attach handlers using Epilog or Prolog.

Primitives

Certain graphic primitives also support EventHandler. Supported primitives include:

They support these patterns:

  • "drag": Makes the primitive draggable and returns coordinates
  • "dragall": Like "drag", but also fires events at drag start and end
  • "click": Captures clicks without the Alt key
  • "altclick": Captures clicks with the Alt key
  • "mousedown": Fires on mouse press
  • "mouseup": Fires on mouse release
  • "mousemove": Captures movement
  • "mouseover": Captures entry into element area
  • "zoom": Captures mouse wheel input

For example, combining "zoom" and "drag", you can manually fit Gaussian curves:

Try moving your mouse wheel on the red dot and then drag it.

You can also create a basic mouse follower using a white rectangle with a "mousemove" pattern:

pt = {0, 0};
Graphics[{
White,
EventHandler[
Rectangle[{-2, -2}, {2, 2}],
{"mousemove" -> Function[xy, pt = xy]}
],
PointSize[0.05], Cyan,
Point[pt // Offload]
}]

a mouse follower

3D Graphics

Currently, event listeners in Graphics3D are limited.

Primitives

The following 3D primitives support EventHandler methods:

They support:

  • "transform": Makes the object draggable and sends an association with a "position" field

This is especially useful for dynamic lighting systems. For example:

point = {1, 1, 1};

Graphics3D[{
Shadows[True],
Polygon[{{-5, 5, -1}, {5, 5, -1}, {5, -5, -1}, {-5, -5, -1}}],
White,
Cuboid[{-1, -1, -1}, {1, 1, 1}],
Shadows[False],
PointLight[Red, {1.5075, 4.1557, 2.6129}, 100],
Shadows[True],
SpotLight[Cyan, point // Offload],

EventHandler[Sphere[point, 0.1], {
"transform" -> Function[assoc, point = assoc["position"]]
}]
}, "Lighting" -> None]